package com.xlf.service.judge.strategy.judgeMachine;

import cn.hutool.core.lang.Assert;
import com.xlf.pojo.base.Judge;
import com.xlf.pojo.base.JudgeCase;
import com.xlf.pojo.base.Problem;
import com.xlf.pojo.base.ProblemCase;
import com.xlf.pojo.dto.RunResult;
import com.xlf.pojo.dto.TestJudgeReq;
import com.xlf.service.judge.exception.CompileError;
import com.xlf.service.judge.io.File;
import com.xlf.service.judge.io.impl.LocalFile;
import com.xlf.service.judge.io.impl.StringFile;
import com.xlf.service.judge.pojo.LanguageConfig;
import com.xlf.service.judge.io.LanguageConfigLoader;
import com.xlf.service.judge.io.Resource;
import com.xlf.service.judge.io.ResourceLoader;
import com.xlf.service.judge.pojo.*;
import lombok.extern.slf4j.Slf4j;

import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;

/**
 * @Author xlf 2734029211@qq.com
 * @Date 2023/9/12 9:02
 * @Description: 抽象判题机策略，屏蔽具体的判题机。
 * @Version 1.0
 */
@Slf4j
public abstract class AbstractJudgeMachineStrategy  implements Compile, Run {


    /**
     * 配置文件加载
     */
    private LanguageConfigLoader languageConfigLoader;

    /**
     * 测试用例加载器，返回具体的加载器
     */
    private ResourceLoader resourceLoader;


    public AbstractJudgeMachineStrategy(LanguageConfigLoader languageConfigLoader, ResourceLoader resourceLoader){
        this.languageConfigLoader = languageConfigLoader;
        this.resourceLoader = resourceLoader;
    }


    @Override
    public Object doCompile(String language,String code) throws CompileError{

        LanguageConfig languageConfig = languageConfigLoader.getLanguageConfigByName(language);

        Assert.notNull(languageConfig,"languageConfig is null !");

        Object res  = compile(languageConfig, code);
        //编译失败
        if (null == res) {
            throw new CompileError("Compile error !", "编译文件未找到！");
        }

        return res;
    }

    /**
     *
     * @param language 语言
     * @param file 执行代码
     * @param testCaseInput 输入
     * @param runLimit 限制
     * @return
     */
    @Override
    public RunResult doRun(String language,
                           Object file,
                           File testCaseInput,
                           RunLimit runLimit) {

        LanguageConfig languageConfig = languageConfigLoader.getLanguageConfigByName(language);

        Assert.notNull(languageConfig,"languageConfig is null !");

        RunDTO runDTO = RunDTO.builder()
                .runLimit(runLimit)
                .file(file)
                .languageConfig(languageConfig)
                .testCaseInput(testCaseInput).build();

        return run(runDTO);

    }


    /**
     *
     * @param languageConfig 语言配置文件
     * @param code 提交的代码
     * @return 编译后的信息，用于运行时使用
     * @throws CompileError 编译失败
     */
    protected abstract Object compile(LanguageConfig languageConfig, String code) throws CompileError;


    /**
     *
     * @return
     */
    protected abstract RunResult run(RunDTO runDTO);


    /**
     * 运行该题目主入口
     * @param problem 题目
     * @param judge 提交的评测纪录
     * @param consumer 也行你需要在编译后做一些操作
     */
    public JudgeResult judge(Problem problem, Judge judge, Consumer<Judge> consumer) throws CompileError {

        //运行结果
        JudgeResult judgeResult = new JudgeResult();

        ArrayList<RunResult> runResults = new ArrayList<>();
        Object compileRes = null;
        try {
            //TODO 1.编译
             compileRes = doCompile(judge.getLanguage(), judge.getCode());

            consumer.accept(judge);

            //TODO 2.获取测试用例文件
            Resource resource = resourceLoader.getResource(problem);
            List<ProblemCase> aCase = resource.getCase();

            //TODO 3.运行
            RunLimit runLimit = RunLimit.builder()
                    .maxTime(problem.getTimeLimit() + 200) //多加200ms
                    .maxMemory(problem.getMemoryLimit())
                    .maxStack(problem.getStackLimit()).build();

            List<JudgeCase> judgeCases;
            judgeCases = new ArrayList<>();

            for (int i = 0; i < aCase.size(); i++) {

                ProblemCase item = aCase.get(i);
                File testCaseInput;
                testCaseInput = new StringFile(item.getInput());

                //运行
                RunResult runResult = doRun(judge.getLanguage(),
                        compileRes,
                        testCaseInput,
                        runLimit
                );

                runResults.add(runResult);
                JudgeCase judgeCase = JudgeCase.builder()
                        .problemId(problem.getId())
                        .submitId(judge.getId())
                        .caseId(item.getId())
                        .userId(judge.getUserId())
                        .time(runResult.getTime())
                        .memory(runResult.getMemory())
                        .score(item.getScore())
                        .status(runResult.getStatus())
                        .inputData(item.getInput())
                        .outputData(item.getOutput())
                        .userOutput(runResult.getFile())
                        .build();
                judgeCases.add(judgeCase);
            }
            judgeResult.setJudgeCase(judgeCases);

        } finally {
            afterHandle(compileRes,runResults);
        }

        return judgeResult;
    }


    /**
     * 测试运行
     * @param testJudgeReq
     * @return
     */
    public RunResult TestJudge(TestJudgeReq testJudgeReq) throws CompileError{

        Object compileResult = null;

        RunResult runResult = null;

        try {
            //TODO 编译
            compileResult = doCompile(testJudgeReq.getLanguage(), testJudgeReq.getCode());

            //TODO 运行
            RunLimit runLimit = RunLimit.builder()
                    .maxMemory(testJudgeReq.getMemoryLimit())
                    .maxTime(testJudgeReq.getTimeLimit() + 200) //多加200ms
                    .maxStack(testJudgeReq.getStackLimit()).build();
            runResult = doRun(testJudgeReq.getLanguage(), compileResult, new StringFile(testJudgeReq.getTestCaseInput()), runLimit);
        } finally {
            afterHandle(compileResult, Collections.singletonList(runResult));
        }

        return runResult;
    }



    /**
     * 也许你的判题机需要在判题后干一些事情，该方法会在最后执行
     * @param file 编译后的文件
     * @param runResult 运行结果
     */
    public abstract void afterHandle(Object file,List<RunResult> runResult);


    /**
     * 通过该接口，你可以得到语言解析器
     * @return
     */
    public LanguageConfigLoader getLanguageConfigLoader() {
        return languageConfigLoader;
    }

    /**
     * 通过该接口，你可以得到测试用例解析器
     * @return
     */
    public ResourceLoader getResourceLoader() {
        return resourceLoader;
    }


}
